Skip to content

《JAVA应用开发》期末试卷A (精选01)

一、辨析题(对的直接打 $\surd$ ,错的打×并改正过来。每小题 2 分,共 20 分)

  1. $1$ 是 java语言中不合法的标识符。( )
查看答案与解析

答案:$\surd$

解析: Java 标识符的命名规则为:由字母、数字、下划线 _ 和美元符号 $ 组成,且不能以数字开头。纯数字 1 以数字开头,不能作为合法的 Java 标识符。因此,题目表述"$1$ 是 java语言中不合法的标识符"是正确的,应打 $\surd$


难度: ⭐ 考点: #Java标识符

💡 学习锦囊

📖 相关公式与知识点:

  • 标识符规则:由字母、数字、_$ 组成,不能以数字开头,不能是关键字,区分大小写。

易错点

  • 标识符不能以数字开头,但可以包含数字(如 a1 合法,1a 不合法)。
🔄 举一反三
  1. 判断:_value 是 Java 中合法的标识符。( )
    查看练习答案与解析

    答案$\surd$解析:以下划线 _ 开头是合法的 Java 标识符。

  1. java基本数据类型中,boolean不能与其他类型相互转换。( )
查看答案与解析

答案:$\surd$

解析: Java 中 boolean 类型只有两个值:truefalse。boolean 类型不能与 int、float、char 等其他基本数据类型进行相互转换。在 C/C++ 中,0 表示 false,非 0 表示 true,但 Java 是强类型语言,boolean 与其他类型之间不存在任何隐式或显式的类型转换关系。


难度: ⭐ 考点: #boolean类型 #类型转换

💡 学习锦囊

📖 相关公式与知识点:

  • boolean 类型:Java 的基本数据类型,只有 truefalse 两个值,不能与数值类型互转。
  • 与 C/C++ 的区别:C 语言中 0 表示 false、非 0 表示 true,Java 严格区分。

易错点

  • 不能用 if (1) 这样的写法,Java 要求条件表达式必须是 boolean 类型。
🔄 举一反三
  1. 判断:Java 中 boolean b = 1; 是合法的。( )
    查看练习答案与解析

    答案:× 解析:Java 中不能将 int 类型的 1 直接赋给 boolean 变量,必须使用 truefalse

  1. java中的字符类型采用的是 Unicode编码,每个字符占32 个比特位。( )
查看答案与解析

答案:×

改正: java中的字符类型采用的是 Unicode编码,每个字符占 16 个比特位。

解析: Java 的 char 类型采用 UTF-16 编码表示 Unicode 字符,每个 char 占 2 个字节,即 16 个比特位(bit),取值范围为 \u0000 ~ \uffff(0~65535)。32 个比特位是 4 个字节,那是 int 类型的大小。


难度: ⭐ 考点: #char类型 #Unicode编码

💡 学习锦囊

📖 相关公式与知识点:

  • char 类型:占 2 字节(16 位),采用 UTF-16 编码,取值范围 0~65535。
  • 各类型大小:byte(1B), short(2B), int(4B), long(8B), float(4B), double(8B), char(2B)。

易错点

  • char 是 16 位(2 字节),不是 32 位。32 位是 int 类型的大小。
🔄 举一反三
  1. 判断:Java 中 int 类型占 4 个字节,即 32 个比特位。( )
    查看练习答案与解析

    答案$\surd$解析:int 占 4 字节(32 位),有符号整数,取值范围为 $-2^{31}$$2^{31}-1$

  1. java 源文件编译后将产生相应的字节码文件,其扩展名为.byte。( )
查看答案与解析

答案:×

改正: java 源文件编译后将产生相应的字节码文件,其扩展名为 .class

解析: Java 源文件(.java)经过 javac 编译器编译后,生成的是字节码文件(.class),而非 .byte.class 文件由 Java 虚拟机(JVM)解释执行。


难度: ⭐ 考点: #字节码文件 #编译流程

💡 学习锦囊

📖 相关公式与知识点:

  • 编译流程.javajavac.class(字节码)→ java → JVM 执行。
  • javac:Java 编译器命令。
  • java:Java 虚拟机启动命令。

易错点

  • 编译后扩展名是 .class,不是 .byte
🔄 举一反三
  1. 判断:运行 Java 字节码文件 Test.class 的命令是 java Test.class。( )
    查看练习答案与解析

    答案:× 解析:运行时应使用 java Test,不加 .class 后缀。

  1. 在java中,除了4 类8种基本数据类型以外,其余都是引用类型。( )
查看答案与解析

答案:$\surd$

解析: Java 的数据类型分为两大类:基本数据类型(Primitive Type)和引用数据类型(Reference Type)。基本数据类型共 4 类 8 种:整数型(byte, short, int, long)、浮点型(float, double)、字符型(char)、布尔型(boolean)。除此之外,所有的类、接口、数组、枚举等都是引用类型。


难度: ⭐ 考点: #基本数据类型 #引用类型

💡 学习锦囊

📖 相关公式与知识点:

  • 8 种基本数据类型:byte(1B), short(2B), int(4B), long(8B), float(4B), double(8B), char(2B), boolean。
  • 引用类型:类、接口、数组、枚举、注解等。

易错点

  • String 是引用类型,不是基本数据类型。
  • Integer、Double 等包装类也是引用类型。
🔄 举一反三
  1. 判断:Java 中数组是基本数据类型。( )
    查看练习答案与解析

    答案:× 解析:数组在 Java 中属于引用类型,通过 new 关键字在堆内存中创建。

  1. 在java中,一个父类可以有多个子类,一个子类也可以有多个父类。( )
查看答案与解析

答案:×

改正: 在java中,一个父类可以有多个子类,一个子类只能有一个直接父类(Java 不支持多继承)。

解析: Java 中类之间的继承是单继承的,即一个子类只能有一个直接父类(使用 extends 关键字)。但一个父类可以被多个子类继承。Java 通过接口(implements)来实现多继承的效果,一个类可以实现多个接口。


难度: ⭐ 考点: #单继承 #接口多实现

💡 学习锦囊

📖 相关公式与知识点:

  • 单继承:一个类只能 extends 一个父类,但可以 implements 多个接口。
  • 接口多继承:一个接口可以 extends 多个接口。

易错点

  • Java 不支持类的多继承,只支持接口的多实现。
🔄 举一反三
  1. 判断:Java 中一个接口可以继承多个接口。( )
    查看练习答案与解析

    答案$\surd$解析:接口支持多继承,如 interface C extends A, B {}

  1. 构造方法的主要作用是完成对类的对象的初始化工作,其返回类型只能是 void。( )
查看答案与解析

答案:×

改正: 构造方法的主要作用是完成对类的对象的初始化工作,构造方法没有返回类型(连 void 也不能写)

解析: 构造方法是一种特殊的方法,它没有返回值类型声明——既不是 void,也不是其他任何类型。如果在构造方法前加了 void,它就变成了一个普通的成员方法,而不再是构造方法。


难度: ⭐ 考点: #构造方法

💡 学习锦囊

📖 相关公式与知识点:

  • 构造方法:无返回类型,方法名与类名相同,用于初始化对象。
  • 默认构造方法:如果类中没有定义任何构造方法,编译器会自动生成无参构造方法。

易错点

  • 构造方法不能写 void,写了 void 就变成了普通方法。
🔄 举一反三
  1. 判断:一个类可以有多个构造方法。( )
    查看练习答案与解析

    答案$\surd$解析:构造方法支持重载,一个类可以定义多个参数列表不同的构造方法。

  1. 在 java 类中,可以用来声明成员访问权限的修饰符有 public、protected 和 private。( )
查看答案与解析

答案:$\surd$

解析: Java 中成员访问权限修饰符共有 4 种:public(公共)、protected(受保护)、默认(不写,包级访问)和 private(私有)。题目中列出了 public、protected 和 private 三种,这三种确实都是可以用来声明成员访问权限的修饰符。虽然漏掉了默认访问权限,但题目说的是"可以用来声明",而非"全部",所以该说法正确。


难度: ⭐ 考点: #访问修饰符

💡 学习锦囊

📖 相关公式与知识点:

  • 访问修饰符(权限从大到小):public > protected > default > private。
  • public:所有类可访问。
  • protected:本包 + 其他包子类可访问。
  • default(不写):本包可访问。
  • private:仅本类可访问。

易错点

  • 除了 public、protected、private,还有默认访问权限(不写任何修饰符)。
🔄 举一反三
  1. 判断:用 private 修饰的成员变量可以被子类直接访问。( )
    查看练习答案与解析

    答案:× 解析:private 成员只能在本类中访问,子类也不能直接访问。

  1. 字符串 String 类中使用 substring 方法来查找该字符串中是否包含某个子串。( )
查看答案与解析

答案:×

改正: 字符串 String 类中使用 contains 方法(或 indexOf 方法)来查找该字符串中是否包含某个子串。substring 方法用于截取子字符串

解析:

  • substring(int beginIndex) / substring(int beginIndex, int endIndex):用于从字符串中截取子串。
  • contains(CharSequence s):用于判断字符串中是否包含指定的字符序列,返回 boolean。
  • indexOf(String str):返回指定子串第一次出现的索引,若不存在则返回 -1。

难度: ⭐ 考点: #String类 #substring #contains

💡 学习锦囊

📖 相关公式与知识点:

  • String 常用方法length(), charAt(), substring(), indexOf(), contains(), equals(), toUpperCase() 等。
  • substring:截取子串。
  • contains/indexOf:查找子串。

易错点

  • substring 是截取子串,contains/indexOf 才是查找子串。
🔄 举一反三
  1. 判断:"Hello".substring(1, 3) 的结果是 "el"。( )
    查看练习答案与解析

    答案$\surd$解析substring(1, 3) 截取索引 1(包含)到索引 3(不包含)的子串,即 "el"

  1. java中的接口是抽象方法和常量值的集合。( )
查看答案与解析

答案:$\surd$

解析: Java 接口(interface)中声明的变量默认是 public static final 的,即常量;声明的方法默认是 public abstract 的,即抽象方法(Java 8 之后也允许 default 方法和 static 方法,但核心本质仍是抽象方法和常量的集合)。因此该说法正确。


难度: ⭐ 考点: #接口

💡 学习锦囊

📖 相关公式与知识点:

  • 接口:变量默认 public static final(常量),方法默认 public abstract(抽象方法)。
  • Java 8+:接口中还可以定义 default 方法和 static 方法(有方法体)。

易错点

  • 接口不能直接实例化(new),但可以通过匿名内部类创建。
🔄 举一反三
  1. 判断:Java 中可以使用 new 关键字创建接口的实例。( )
    查看练习答案与解析

    答案:× 解析:接口不能直接实例化,但可以通过匿名内部类的方式创建接口的实例(本质是创建了一个实现了该接口的匿名类的对象)。

二、简答题(每小题 6 分,共 30 分)

  1. 简述 StringBuffer 类的常用方法及其与 String 类的区别。
查看答案与解析

答案:

StringBuffer 常用方法:

  • append(String s):在末尾追加字符串。
  • insert(int offset, String s):在指定位置插入字符串。
  • delete(int start, int end):删除指定范围的字符。
  • reverse():反转字符串。
  • replace(int start, int end, String s):替换指定范围的字符。
  • toString():转换为 String 对象。
  • length() / capacity():获取长度 / 容量。

StringBuffer 与 String 的区别:

比较维度StringStringBuffer
可变性不可变(immutable)可变(mutable)
线程安全安全(不可变天然安全)安全(方法用 synchronized 修饰)
性能频繁拼接会产生大量临时对象频繁拼接性能更高
存储字符串常量池堆内存

难度: ⭐⭐ 考点: #StringBuffer #String

💡 学习锦囊

📖 相关公式与知识点:

  • String vs StringBuffer vs StringBuilder:String 不可变,StringBuffer 线程安全可变,StringBuilder 非线程安全可变(性能最高)。
  • StringBuffer 常用方法:append, insert, delete, reverse, replace, toString。

易错点

  • StringBuffer 和 StringBuilder 的区别常被混淆(前者线程安全,后者不是)。
🔄 举一反三
  1. 简述 StringBuilder 与 StringBuffer 的区别。
    查看练习答案与解析

    答案

    • StringBuffer:线程安全,方法使用 synchronized 修饰,适用于多线程环境,性能略低。
    • StringBuilder:非线程安全,方法没有同步,适用于单线程环境,性能更高。
    • 两者 API 几乎完全相同,都继承自 AbstractStringBuilder
  1. 什么是servlet?常用的开发方式有哪些?
查看答案与解析

答案:

Servlet 定义: Servlet 是运行在 Web 服务器或应用服务器上的 Java 程序,用于处理客户端(浏览器)的 HTTP 请求并生成响应。它是 Java EE 规范中的核心组件,充当 Web 应用的控制层。

常用开发方式:

  1. 实现 Servlet 接口:直接实现 javax.servlet.Servlet 接口,需实现所有方法(init, service, destroy 等)。
  2. 继承 GenericServlet:继承 javax.servlet.GenericServlet 抽象类,只需重写 service() 方法,协议无关。
  3. 继承 HttpServlet(最常用):继承 javax.servlet.http.HttpServlet,根据请求方式重写 doGet(), doPost(), doPut(), doDelete() 等方法。
  4. 注解配置:使用 @WebServlet 注解替代 web.xml 配置。

难度: ⭐⭐ 考点: #Servlet

💡 学习锦囊

📖 相关公式与知识点:

  • Servlet 生命周期:加载 → 实例化 → init() → service() → doGet/doPost() → destroy()。
  • HttpServlet:最常用的 Servlet 开发方式,重写 doGet/doPost 方法。

易错点

  • Servlet 是服务器端程序,不是客户端程序。
🔄 举一反三
  1. 简述 Servlet 的生命周期。
    查看练习答案与解析

    答案

    1. 加载和实例化:Servlet 容器加载 Servlet 类并创建实例。
    2. 初始化:调用 init() 方法,只执行一次。
    3. 处理请求:调用 service() 方法,根据请求类型分发到 doGet/doPost 等。
    4. 销毁:调用 destroy() 方法,释放资源。
  1. 简述请求转发和请求重定向的区别。
查看答案与解析

答案:

请求转发(Forward)与请求重定向(Redirect)的区别:

比较维度请求转发 (Forward)请求重定向 (Redirect)
请求次数1 次请求2 次请求
地址栏不变(显示原 URL)改变(显示新 URL)
数据共享同一个 request,可共享数据不同 request,不能共享数据
跳转范围仅限服务器内部可跳转到外部 URL
实现方式request.getRequestDispatcher().forward()response.sendRedirect()
速度较快较慢(多一次网络往返)

难度: ⭐⭐ 考点: #请求转发 #请求重定向

💡 学习锦囊

📖 相关公式与知识点:

  • 转发:服务器内部行为(request.getRequestDispatcher),一次请求,地址栏不变。
  • 重定向:让浏览器重新发请求(response.sendRedirect),两次请求,地址栏改变。

易错点

  • 请求转发地址栏不变,重定向地址栏改变——这是最直观的区分方式。
🔄 举一反三
  1. 在 Servlet 中,如何将请求转发到 target.jsp
    查看练习答案与解析

    答案request.getRequestDispatcher("target.jsp").forward(request, response);解析:通过 RequestDispatcher 的 forward 方法实现服务器内部转发。

  1. 简述至少两种防止SQL注入漏洞的常用方法。
查看答案与解析

答案:

防止 SQL 注入的常用方法:

  1. 使用 PreparedStatement(预编译语句)

    • 将 SQL 语句与参数分离,参数使用占位符 ?
    • 数据库会预先编译 SQL 模板,参数值不会被当作 SQL 代码执行。
    • 示例:PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name=?"); ps.setString(1, userName);
  2. 输入验证与过滤

    • 对用户输入进行严格的格式校验(如正则表达式)。
    • 过滤或转义特殊字符(如单引号 '、分号 ;、注释符 -- 等)。
  3. 使用 ORM 框架(如 Hibernate、MyBatis):

    • ORM 框架内部通常使用参数化查询,能有效防止 SQL 注入。
  4. 最小权限原则

    • 数据库用户只授予必要的权限,避免使用 root/sa 等高权限账户连接数据库。

难度: ⭐⭐ 考点: #SQL注入 #PreparedStatement

💡 学习锦囊

📖 相关公式与知识点:

  • SQL 注入原理:攻击者通过拼接恶意 SQL 代码来操纵数据库。核心防御是参数化查询。
  • PreparedStatement:预编译 SQL,参数使用 ? 占位符,通过 setXxx() 方法设置参数值。

易错点

  • PreparedStatement 是防 SQL 注入的核心手段,不要只说"过滤特殊字符"。
🔄 举一反三
  1. 以下代码是否存在 SQL 注入风险?Statement stmt = conn.createStatement(); stmt.executeQuery("SELECT * FROM users WHERE name='" + userName + "'");
    查看练习答案与解析

    答案:存在风险。 解析:使用 Statement 拼接字符串的方式构建 SQL,如果 userName 包含恶意代码(如 ' OR '1'='1),会导致 SQL 注入。应改用 PreparedStatement。

  1. 简述http协议中至少 3种常用的请求头。
查看答案与解析

答案:

HTTP 协议常用请求头(至少 3 种):

  1. Host:指定请求的服务器域名和端口号(HTTP/1.1 必须)。例如:Host: www.example.com
  2. User-Agent:标识客户端浏览器及操作系统信息。例如:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
  3. Content-Type:指定请求体的 MIME 类型。例如:Content-Type: application/jsonapplication/x-www-form-urlencoded
  4. Accept:告知服务器客户端能处理的媒体类型。例如:Accept: text/html, application/json
  5. Cookie:携带客户端的 Cookie 信息发送给服务器。例如:Cookie: JSESSIONID=abc123
  6. Authorization:携带认证凭证信息。例如:Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
  7. Referer:指明请求来源页面的 URL。

难度: ⭐⭐ 考点: #HTTP协议 #请求头

💡 学习锦囊

📖 相关公式与知识点:

  • HTTP 请求结构:请求行 + 请求头 + 空行 + 请求体。
  • 常见请求头:Host, User-Agent, Content-Type, Accept, Cookie, Authorization, Referer。

易错点

  • Host 是 HTTP/1.1 必须的请求头,不能省略。
🔄 举一反三
  1. 简述 GET 请求和 POST 请求的区别。
    查看练习答案与解析

    答案

    • GET:参数拼接在 URL 后面,有长度限制(约 2KB),数据可见,适合查询操作,可被缓存和收藏。
    • POST:参数放在请求体中,无长度限制,数据不可见,适合提交/修改操作,不可缓存。
    • GET 是幂等的(多次请求结果相同),POST 不是幂等的。

三、程序填空题(每空 3分,共 30 分)

  1. 下面的程序对"百鸡百钱"问题进行了求解,公鸡每只 3 元,母鸡每只 5 元,小鸡 3只 1元,用 100元钱买100只鸡,公鸡、母鸡、小鸡应各买多少?请将程序填写完整。
java
public class Blank1 {
    public static void main(String[] args) {
        int a, b, c;
        for (a = 0; ______; a++) {
            for (b = 0; ______; b++) {
                c = 100 - a - b;
                if ((3 * a + 5 * b + c / 3 == 100) && (______))
                    System.out.println("公鸡:" + a + "母鸡:" + b + "小鸡:" + c);
            }
        }
    }
}
查看答案与解析

答案:

  • 空1:a <= 33(或 a < 34,因为公鸡最多买 100/3 ≈ 33 只)
  • 空2:b <= 20(或 b < 21,因为母鸡最多买 100/5 = 20 只)
  • 空3:c % 3 == 0(小鸡 3 只 1 元,所以小鸡数量必须是 3 的倍数)

解析: "百鸡百钱"问题:公鸡 3 元/只,母鸡 5 元/只,小鸡 3 只/1 元(即 1/3 元/只)。100 元买 100 只鸡。

设公鸡 a 只,母鸡 b 只,小鸡 c 只,则:

  • $a + b + c = 100$(数量约束)
  • $3a + 5b + c/3 = 100$(金额约束)

由第一个等式得 $c = 100 - a - b$,代入第二个等式。

公鸡最多买 $\lfloor 100/3 \rfloor = 33$ 只,母鸡最多买 $\lfloor 100/5 \rfloor = 20$ 只。

由于小鸡 3 只 1 元,c 必须是 3 的倍数,即 c % 3 == 0


难度: ⭐⭐ 考点: #百鸡百钱 #穷举法

💡 学习锦囊

📖 相关公式与知识点:

  • 百鸡百钱:经典的不定方程问题,通过穷举法(枚举)求解,利用约束条件缩小搜索范围。
  • 枚举优化:利用约束条件缩小循环范围,公鸡最多 33 只,母鸡最多 20 只。

易错点

  • 百鸡百钱中容易忘记 c % 3 == 0 这个条件,导致出现非整数的小鸡数量。
🔄 举一反三
  1. 如果公鸡 5 元/只,母鸡 3 元/只,小鸡 1/3 元/只,100 元买 100 只鸡,公鸡最多买多少只?
    查看练习答案与解析

    答案:20 只 解析$\lfloor 100/5 \rfloor = 20$,公鸡最多买 20 只。

  1. 请根据注释提示将下面的程序填写完整。
java
public class Blank2 {
    public static void main(String args[]) {
        String s1 = new String("2012");
        String s2 = new String("100.50");
        int x = ______;          //将s1转换为int类型
        double y = ______;       //将s2转换为double类型
        double z = x + y;
        String s3 = ______;      //将z转换为字符串
        StringBuffer sbr = new StringBuffer("Thingking");
        String s4 = new String("in Java");
        ______;                  //将s4连接在sbr的后面
        System.out.println(sbr.toString()); //显示为Thingking in Java
    }
}
查看答案与解析

答案:

  • 空1:Integer.parseInt(s1)
  • 空2:Double.parseDouble(s2)
  • 空3:String.valueOf(z)(或 Double.toString(z)"" + z
  • 空4:sbr.append(s4)

解析:

  • Integer.parseInt(String s):将字符串解析为 int 类型。
  • Double.parseDouble(String s):将字符串解析为 double 类型。
  • String.valueOf(double d):将 double 转换为 String。
  • StringBuffer.append(String s):在 StringBuffer 末尾追加字符串,直接修改原对象。

难度: ⭐⭐ 考点: #类型转换 #Integer.parseInt #Double.parseDouble #StringBuffer

💡 学习锦囊

📖 相关公式与知识点:

  • 类型转换
    • 字符串 → 基本类型:Integer.parseInt(), Double.parseDouble(), Boolean.parseBoolean() 等。
    • 基本类型 → 字符串:String.valueOf(), Integer.toString(), 或使用 "" + 数值
  • StringBuffer.append():直接在原对象上修改,返回 this,支持链式调用。

易错点

  • parseIntparseDouble 是静态方法,通过类名调用。
  • StringBuffer 的 append 直接修改原对象,不需要接收返回值。
🔄 举一反三
  1. 将字符串 "3.14" 转换为 float 类型,应使用什么方法?
    查看练习答案与解析

    答案Float.parseFloat("3.14")解析:与 Integer.parseInt 和 Double.parseDouble 类似,Float 类也提供了 parseFloat(String s) 静态方法。

  1. 下面的程序是采用冒泡法对数组元素按从小到大的顺序排序,请将程序填写完整。
java
public class Blank3 {
    public static void main(String[] args) {
        int[] a = new int[]{21, 34, 211, 15, 92, 68, 89, 794, 11, 863};
        int temp;
        for (int i = 0; i < 10; i++)
            for (int j = 0; j < ______; j++)
                if (a[j] > a[j + 1]) {
                    temp = a[j];
                    ______;
                    ______;
                }
        for (int i = 0; i < a.length; i++)
            System.out.print(a[i] + " ");
    }
}
查看答案与解析

答案:

  • 空1:9 - i(或 a.length - 1 - i
  • 空2:a[j] = a[j + 1]
  • 空3:a[j + 1] = temp

解析: 冒泡排序的核心思想:每一轮将当前未排序部分的最大值"冒泡"到最后。

  • 外层循环控制轮数(共 n-1 轮)。
  • 内层循环进行相邻元素比较交换,每轮比较范围递减(因为每轮结束后最后 i 个元素已排好)。
  • 第 i 轮时,需要比较前 n - 1 - i 对相邻元素,即 j < 9 - i(n=10)。
  • 交换三部曲:temp 暂存 → 赋值 → 赋值。

难度: ⭐⭐ 考点: #冒泡排序

💡 学习锦囊

📖 相关公式与知识点:

  • 冒泡排序:时间复杂度 $O(n^2)$,空间复杂度 $O(1)$,稳定排序。每轮将最大元素移到末尾。
  • 交换三部曲:temp = a[j]; a[j] = a[j+1]; a[j+1] = temp;

易错点

  • 冒泡排序内层循环边界是 n - 1 - i,不是 n - 1
🔄 举一反三
  1. 用冒泡排序对数组 {5, 2, 8, 1, 9} 按从大到小排序,写出内层 if 条件。
    查看练习答案与解析

    答案if (a[j] < a[j + 1])(将 > 改为 < 即可实现降序排列) 解析:冒泡排序的升序和降序只取决于比较符号的方向。升序用 >(大的往后移),降序用 <(小的往后移)。

四、编程题(每小题 10 分,共20分)

  1. 编写一个程序,对有序数列进行二分查找。
查看答案与解析

答案:

java
public class BinarySearch {
    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 11, 13, 15};
        int target = 7;
        int result = binarySearch(arr, target);
        if (result != -1) {
            System.out.println("找到目标 " + target + ",索引为 " + result);
        } else {
            System.out.println("未找到目标 " + target);
        }
    }
}

解析:

第一步:理解二分查找原理 二分查找要求数组有序。每次取中间元素与目标值比较,若相等则找到;若中间值小于目标值,说明目标在右半部分;若中间值大于目标值,说明目标在左半部分。

第二步:确定算法步骤

  1. 初始化左指针 left = 0,右指针 right = n - 1
  2. left <= right 时循环:
    • 计算中间位置 mid = left + (right - left) / 2(避免整数溢出)。
    • arr[mid] == target,找到目标,返回 mid。
    • arr[mid] < target,目标在右半部分,left = mid + 1
    • arr[mid] > target,目标在左半部分,right = mid - 1
  3. 循环结束未找到,返回 -1。

第三步:分析边界条件

  • 使用 left <= right 而非 left < right,确保单个元素也能被检查。
  • 使用 mid = left + (right - left) / 2 而非 (left + right) / 2,防止 left + right 溢出。

难度: ⭐⭐ 考点: #二分查找

💡 学习锦囊

📖 相关公式与知识点:

  • 二分查找时间复杂度$O(\log_2 n)$,每次比较将搜索范围减半。
  • 二分查找前提:数组必须有序(升序或降序)。
  • mid 安全计算mid = left + (right - left) / 2 防止整数溢出。

易错点

  • 二分查找中忘记 mid ± 1,导致死循环。
  • 循环条件应为 left <= right,不是 left < right
🔄 举一反三
  1. 编写递归版本的二分查找算法。
    查看练习答案与解析
    java
    public static int binarySearchRecursive(int[] arr, int target, int left, int right) {
        if (left > right) return -1;
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) return mid;
        if (arr[mid] < target)
            return binarySearchRecursive(arr, target, mid + 1, right);
        else
            return binarySearchRecursive(arr, target, left, mid - 1);
    }

    解析:递归版本将循环改为函数自调用,基准条件是 left > right 时返回 -1。递归深度为 $O(\log n)$

  1. 编写一个servlet,实现对文件名中包含中文的文件进行下载的功能,并写出其配置代码。
查看答案与解析

答案:

java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.URLEncoder;

@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String fileName = request.getParameter("filename");
        if (fileName == null || fileName.trim().isEmpty()) {
            response.getWriter().write("文件名不能为空");
            return;
        }

        String filePath = getServletContext().getRealPath("/files/") + File.separator + fileName;
        File file = new File(filePath);

        if (!file.exists()) {
            response.getWriter().write("文件不存在");
            return;
        }

        response.setContentType(getServletContext().getMimeType(fileName));
        response.setContentLengthLong(file.length());

        String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
                .replaceAll("\\+", "%20");
        response.setHeader("Content-Disposition",
                "attachment; filename*=UTF-8''" + encodedFileName);

        try (FileInputStream fis = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        }
    }
}

web.xml 配置(或使用 @WebServlet 注解):

xml
<servlet>
    <servlet-name>FileDownloadServlet</servlet-name>
    <servlet-class>FileDownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>FileDownloadServlet</servlet-name>
    <url-pattern>/download</url-pattern>
</servlet-mapping>

解析:

第一步:理解中文文件名下载的核心问题 HTTP 响应头中直接包含中文字符会导致乱码,因为 HTTP 头默认只支持 ASCII 字符。需要对中文文件名进行 URL 编码。

第二步:关键处理步骤

  1. 使用 URLEncoder.encode(fileName, "UTF-8") 对文件名进行编码。
  2. 将编码后的 + 替换为 %20(空格的正确编码)。
  3. 设置 Content-Disposition 头,使用 filename*=UTF-8'' 格式(RFC 5987)来支持中文文件名。
  4. 设置正确的 Content-TypeContent-Length
  5. 使用文件输入流读取文件,通过响应输出流写入客户端。

第三步:配置说明

  • 使用 @WebServlet("/download") 注解或 web.xml 配置 Servlet 映射。
  • 文件存放在 Web 应用的 /files/ 目录下。

难度: ⭐⭐ 考点: #Servlet #文件下载 #中文编码 #URLEncoder #Content-Disposition

💡 学习锦囊

📖 相关公式与知识点:

  • Servlet 文件下载核心步骤
    1. 设置 Content-Type(MIME 类型)。
    2. 设置 Content-Disposition: attachment 告诉浏览器下载而非直接打开。
    3. 将文件内容写入 response.getOutputStream()
  • 中文文件名处理URLEncoder.encode() + RFC 5987 格式 filename*=UTF-8''

易错点

  • 文件下载后不关闭流,导致资源泄露(使用 try-with-resources)。
  • 中文文件名直接用 filename="中文.pdf" 会导致乱码,必须使用 RFC 5987 编码格式。
🔄 举一反三
  1. 如何在 Servlet 中实现文件上传功能?
    查看练习答案与解析

    答案:使用 @MultipartConfig 注解标记 Servlet,通过 request.getPart("fileFieldName") 获取上传的文件 Part 对象,再调用 part.write(filePath) 保存到服务器。需要在表单中设置 enctype="multipart/form-data"

你正在阅读的是会员专属文档,💕 限时特惠进行中
你尚未登录,目前新用户可获3天体验会员,去登录